﻿using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Markup;
using Ops.Mpc.Controllers;
using Ops.Mpc.States;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Ops.Mpc.Models;

namespace Ops.Mpc.Presenters
{
    [ContentProperty("Controller")]
    [TemplatePart(Name=ContentPresentationSite1Part,Type=typeof(ContentPresenter))]
    [TemplatePart(Name = ContentPresentationSite2Part, Type = typeof(ContentPresenter))]
    [TemplatePart(Name=PanelPart,Type= typeof(Panel))]
    public class Presenter : ContentControl
    {
        #region Fields & Properties

        private const string ContentPresentationSite1Part = "ContentPresentationSite1";
        private const string ContentPresentationSite2Part = "ContentPresentationSite2";
        private const string PanelPart = "Panel";
        private StateTemplate _currentStateTemplate = null;
        private Storyboard _currentStoryboard;
        private bool _contentPresenterToggle = false;
        private ContentPresenter _contentPresentationSite1;
        private ContentPresenter _contentPresentationSite2;
        private List<Timeline> _inTimelines = new List<Timeline>();
        private List<Timeline> _outTimelines = new List<Timeline>();
        private DataTemplate _blankDataTemplate;
        private Panel _panel = null;


        public static readonly DependencyProperty TransitionTargetProperty = DependencyProperty.RegisterAttached("TransitionTarget", typeof(TransitionTarget), typeof(Presenter), new PropertyMetadata(TransitionTarget.NewContentPresenter));
        
        public static void SetTransitionTarget(DependencyObject dependencyObject, TransitionTarget tranisitionTarget)
        {
            dependencyObject.SetValue(TransitionTargetProperty, tranisitionTarget);
        }

        public static TransitionTarget GetTransitionTarget(DependencyObject dependencyObject)
        {
            return (TransitionTarget) dependencyObject.GetValue(TransitionTargetProperty);
        }

        public static readonly DependencyProperty ControllerProperty = DependencyProperty.Register("Controller", typeof(IController), typeof(Presenter), new PropertyMetadata(new PropertyChangedCallback(OnControllerChanged)));
        public IController Controller
        {
            get
            {
                return (IController)this.GetValue(ControllerProperty);
            }

            set
            {
                this.SetValue(ControllerProperty, value);
            }
        }


        public static readonly DependencyProperty CurrentStateProperty = DependencyProperty.Register("CurrentState", typeof(IState), typeof(Presenter), new PropertyMetadata(new PropertyChangedCallback(OnCurrentStateChanged)));
        public IState CurrentState
        {
            get
            {
                return (IState)this.GetValue(CurrentStateProperty);
            }

            set
            {
                this.SetValue(CurrentStateProperty, value);
            }
        }

        private Storyboard CurrentStoryboard
        {
            get { return _currentStoryboard; }
            set
            {
                // decouple event
                if (_currentStoryboard != null)
                {
                    _currentStoryboard.Completed -= OnTransitionCompleted;
                }

                _currentStoryboard = value;

                if (_currentStoryboard != null)
                {
                    _currentStoryboard.Completed += OnTransitionCompleted;
                }
            }
        }

        private ContentPresenter PreviousContentPresentationSite
        {
            get
            {
                if (_contentPresenterToggle)
                    return this._contentPresentationSite1;
                else
                    return this._contentPresentationSite2;
            }
        }

        private ContentPresenter CurrentContentPresentationSite
        {
            get
            {
                if (!_contentPresenterToggle)
                    return this._contentPresentationSite1;
                else
                    return this._contentPresentationSite2;
            }
        }



        #endregion

        #region Constructors
        public Presenter()
        {
            this.DefaultStyleKey = typeof(Presenter);
            this.Loaded += new RoutedEventHandler(OnLoaded);
            this.Unloaded += new RoutedEventHandler(OnUnloaded);


            Resolver resolver = new Resolver(this);
            this.Resources.Add("Resolver", resolver);

        }



 


        #endregion

        #region Methods


        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            this._contentPresentationSite1 = (ContentPresenter)this.GetTemplateChild(ContentPresentationSite1Part);
            this._contentPresentationSite2 = (ContentPresenter)this.GetTemplateChild(ContentPresentationSite2Part);
            this._blankDataTemplate = this._contentPresentationSite1.ContentTemplate;
            this._panel = (Panel)this.GetTemplateChild(PanelPart);
            this.SetStateTemplate(this._currentStateTemplate,this.CurrentState,this.CurrentState);


        }

        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            if (this.Controller != null)
                this.Controller.Start();
        }

        private void OnUnloaded(object sender, RoutedEventArgs e)
        {
            if (this.Controller != null)
                this.Controller.Stop();
        }

        private static void OnControllerChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
        {
            Presenter presenter = dependencyObject as Presenter;
            presenter.SetBinding(CurrentStateProperty, new Binding("CurrentState") { Source = presenter.Controller });

            if (presenter.Controller != null)
                presenter.Controller.Presenter = presenter;

            presenter.Content = presenter.Controller;
        }

        private void OnStateDefinitionsChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            this.SwitchStateDefinitions(this.CurrentState,this.CurrentState);
        }


        private static void OnCurrentStateChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
        {
            ((Presenter)dependencyObject).SwitchStateDefinitions(dependencyPropertyChangedEventArgs.OldValue as IState,dependencyPropertyChangedEventArgs.NewValue as IState);
        }

        private void SwitchStateDefinitions(IState oldState, IState newState)
        {
            if (oldState != newState)
            {
                if (oldState != null)
                    oldState.OnLeavingState();
                if (newState != null)
                    newState.OnEnteringState();
            }

            if (this.CurrentState != null && this.CurrentState.StateTemplate != null)
                this.CurrentState.StateTemplate.Execute(this, oldState, newState);

        }

        protected Storyboard GetStoryboard(string storyboardName)
        {
            //if (this.Transitions != null)
            //    foreach (Storyboard storyboard in this.Transitions)
            //        if (((string)storyboard.GetValue(FrameworkElement.NameProperty)) == storyboardName)
            //            return storyboard;
            //return null;

            return this._panel.Resources[storyboardName] as Storyboard;
        }

        public bool SetContentVisualState(string targetName, string visualStateName)
        {
            bool result = false;

            if (this.CurrentContentPresentationSite != null)
            {
               Control control = this.GetCurrentStateTemplateChild(targetName) as Control;

               if (control != null)
                  result = VisualStateManager.GoToState(control, visualStateName, true);
            }

            return result;

        }

        public bool SetParentVisualState(string visualStateName)
        {
            FrameworkElement frameworkElement = this;

            while (frameworkElement != null)
            {
                foreach (VisualStateGroup visualStateGroup in VisualStateManager.GetVisualStateGroups(frameworkElement))
                {
                    foreach (VisualState visualState in visualStateGroup.States)
                        if (visualState.Name == visualStateName)
                        {
                            Control control = this.GetParentControl(frameworkElement);
                            return VisualStateManager.GoToState(control, visualStateName, true);
                        }
                }

                frameworkElement = this.GetParentFrameworkElement(frameworkElement);
            }

            return false;
        }

        public void SetStateTemplate(StateTemplate stateTemplate, IState oldState, IState newState)
        {
            this._currentStateTemplate = stateTemplate;

            if (this.CurrentStoryboard != null)
                    this.CurrentStoryboard.Stop();

            if (this._currentStateTemplate != null && this._contentPresentationSite1 != null && this._contentPresentationSite2 != null)
            {
                this.TogglePresenter();

                this.CurrentStoryboard = this.GetStoryboard(this.GetTransition(oldState, newState).ToString());

                //this.CurrentContentPresentationSite.Opacity = 0;
                this.CurrentContentPresentationSite.ContentTemplate = stateTemplate;

                if (stateTemplate.Style != null)
                    this.CurrentContentPresentationSite.Style = stateTemplate.Style;

                this.PreviousContentPresentationSite.SetValue(Canvas.ZIndexProperty, 0);
                this.CurrentContentPresentationSite.SetValue(Canvas.ZIndexProperty, 1);

                if (this.CurrentStoryboard != null)
                {
                    this.CurrentStoryboard.Stop();

                    foreach (Timeline timeline in _currentStoryboard.Children)
                    {
                        if (Presenter.GetTransitionTarget(timeline) == TransitionTarget.NewContentPresenter)
                            Storyboard.SetTarget(timeline, CurrentContentPresentationSite);
                        else
                            Storyboard.SetTarget(timeline, PreviousContentPresentationSite);
                    }

                    this.CurrentStoryboard.Begin();
                }
                else
                    this.CompleteContentSwitch();

            }

        }

        public Transition GetTransition(IState oldState, IState newState)
        {
            string oldStateName = oldState == null ? "*" : oldState.ItemName;
            string newStateName = newState == null ? "*" : newState.ItemName;

            foreach (StateTransition stateTransition in this.Controller.StateTransitions)
                 if (stateTransition.From == oldStateName && stateTransition.To == newStateName)
                    return stateTransition.Transition;

            foreach (StateTransition stateTransition in this.Controller.StateTransitions)
                if (stateTransition.From == "*" && stateTransition.To == newStateName)
                    return stateTransition.Transition;

            foreach (StateTransition stateTransition in this.Controller.StateTransitions)
                if (stateTransition.From == oldStateName && stateTransition.To == "*")
                    return stateTransition.Transition;
            
            foreach (StateTransition stateTransition in this.Controller.StateTransitions)
                if (stateTransition.From == "*" && stateTransition.To == "*")
                    return stateTransition.Transition;

            return Transition.None;
        }

        private void CompleteContentSwitch()
        {
            this.PreviousContentPresentationSite.ContentTemplate = this._blankDataTemplate;
            this.Controller.ExecuteStateAutoRun();
        }

        private void OnTransitionCompleted(object sender, EventArgs e)
        {
            this.CompleteContentSwitch();
        }

        public void TogglePresenter()
        {
            this._contentPresenterToggle = !this._contentPresenterToggle;
        }

        protected FrameworkElement GetParentFrameworkElement(DependencyObject dependencyObject)
        {
            DependencyObject parent = VisualTreeHelper.GetParent(dependencyObject);
            FrameworkElement parentFrameworkElement = parent as FrameworkElement;

            while (parent != null && parentFrameworkElement == null)
            {
                parent = VisualTreeHelper.GetParent(parent);
                parentFrameworkElement = parent as Control;
            }

            return parentFrameworkElement;


        }

        protected Control GetParentControl(DependencyObject dependencyObject)
        {
            DependencyObject parent = VisualTreeHelper.GetParent(dependencyObject);
            Control control = parent as Control;

            while (parent != null && control == null)
            {
                parent = VisualTreeHelper.GetParent(parent);
                control = parent as Control;
            }

            return control;


        }

        internal Presenter GetParentPresenter()
        {
            return GetParentPresenter(this);
        }

        public static Presenter GetParentPresenter(DependencyObject dependencyObject)
        {
            IPresenterRouter iPresenterRouter = dependencyObject as IPresenterRouter;

            if (iPresenterRouter != null)
                return iPresenterRouter.GetPresenter();
            else
            {
                DependencyObject parent = VisualTreeHelper.GetParent(dependencyObject);
                Presenter presenter = parent as Presenter;
                
                while (parent != null && presenter == null)
                {
                    iPresenterRouter = parent as IPresenterRouter;

                    if (iPresenterRouter != null)
                        return iPresenterRouter.GetPresenter();

                    parent = VisualTreeHelper.GetParent(parent);
                    presenter = parent as Presenter;
                }

                return presenter;
            }


        }

        internal IStateAction GetParentStateAction(string stateActionName)
        {
            Presenter presenter = this.GetParentPresenter();

            if (presenter != null && presenter.Controller != null && presenter.Controller.CurrentState != null)
                return presenter.Controller.CurrentState.GetStateAction(stateActionName,true);
            else
                return null;


        }

        /// <summary>
        /// Gets the parent model.
        /// </summary>
        /// <param name="modelName">Name of the model.</param>
        /// <returns></returns>
        internal Model GetParentModel(string modelName)
        {
            Presenter presenter = this.GetParentPresenter();

            if (presenter != null && presenter.Controller != null)
                return presenter.Controller.GetModel(modelName);
            else if (presenter == null)
                Debug.WriteLine("Presenter {0} could not find parent presenter", this.Name);
            else
                Debug.WriteLine("Presenter {0} could not find parent presenter controller", this.Name);

            return null;

        }



        /// <summary>
        /// Gets the current state template child.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <returns></returns>
        public DependencyObject GetCurrentStateTemplateChild(string name)
        {
            return this.GetChild(this.CurrentContentPresentationSite, name);
        }

        /// <summary>
        /// Gets the current state template child.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <returns></returns>
        public DependencyObject GetCurrentStateTemplateChild(Type type)
        {
            return this.GetChild(this.CurrentContentPresentationSite, type);
        }

        /// <summary>
        /// Gets the child.
        /// </summary>
        /// <param name="parentDependencyObject">The parent dependency object.</param>
        /// <param name="name">The name.</param>
        /// <returns></returns>
        private DependencyObject GetChild(DependencyObject parentDependencyObject, string name)
        {
            int count = VisualTreeHelper.GetChildrenCount(parentDependencyObject);

            for (int index = 0; index < count; index++)
            {
                DependencyObject dependencyObject = VisualTreeHelper.GetChild(parentDependencyObject, index);

                if (dependencyObject != null)
                {
                    string childName = (string)dependencyObject.GetValue(FrameworkElement.NameProperty);

                    if (!string.IsNullOrEmpty(childName) && childName == name)
                        return dependencyObject;
                    else
                    {
                        DependencyObject childDependencyObject = this.GetChild(dependencyObject, name);
                        if (childDependencyObject != null)
                            return childDependencyObject;

                    }
                }
            }

            return null;
        }

        /// <summary>
        /// Gets the child.
        /// </summary>
        /// <param name="parentDependencyObject">The parent dependency object.</param>
        /// <param name="type">The type of child to get.</param>
        /// <returns></returns>
        private DependencyObject GetChild(DependencyObject parentDependencyObject, Type type)
        {
            int count = VisualTreeHelper.GetChildrenCount(parentDependencyObject);

            for (int index = 0; index < count; index++)
            {
                DependencyObject dependencyObject = VisualTreeHelper.GetChild(parentDependencyObject, index);

                if (dependencyObject != null)
                {
                    if (type.IsInstanceOfType(dependencyObject))
                        return dependencyObject;
                    else
                    {
                        DependencyObject childDependencyObject = this.GetChild(dependencyObject, type);
                        if (childDependencyObject != null)
                            return childDependencyObject;

                    }
                }
            }

            return null;
        }

        #endregion
    }
}
